#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/printk.h>
#include <linux/kern_levels.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#define GLOBAMEM_SIZE 0x1000
#define GLOBAMEM_MAJOR 230
//#define GLOBAMEM_MAJOR 0
#define MEM_CLEN_CMD    0x01
#define DEVICE_NUM      10

#define PLATFORM_DRIVER  

static int globalmemfifo_major = GLOBAMEM_MAJOR;
/**
 * #define S_IRUGO		(S_IRUSR|S_IRGRP|S_IROTH)
 * #define S_IRUSR 00400  user read
 * #define S_IRGRP 00040  user grep read
 * #define S_IROTH 00004  oter  read 
 * S_IRUGO = 0x00444
*/
//module_parem(globamem_major,int,S_IRUGO);  


struct globalmemfifo_dev{
    struct cdev cdev;
    unsigned int current_len;
    unsigned char mem[GLOBAMEM_SIZE];
    struct mutex m_mutex;
    wait_queue_head_t r_wait;
    wait_queue_head_t w_wait;
    struct fasync_struct *async_queue;
};

struct globalmemfifo_dev* globalmemfifo_devp;
/**
 * 用来修改一个文件当前读写位置
 * 并将新的位置返回
 * 在出错的时候返回一个负值
 * 0 set now offset
 * 1 add now offset
 * -EINVAL 无效参数
*/
static loff_t globalmemfifo_llseek (struct file * file, loff_t  offset, int orig)
{
    loff_t ret =0;
    switch (orig)
    {
    case 0:
        if((offset < 0) || (offset > GLOBAMEM_SIZE))
        ret = -EINVAL;
         file->f_pos = (unsigned int)offset;
        break;
    case 1:
         if((file->f_pos + offset < 0) || (file->f_pos+offset > GLOBAMEM_SIZE))
        ret = -EINVAL;
         file->f_pos = (unsigned int)offset;
        break;
    default:
        ret = -EINVAL;
        break;
    }
    return ret;
}
/**
 * 从设备中读取数据  返回0 end-of-file(EOF) 
 * EFAULT   ----   It happen if the memory address of some argument 
 * passed to sendto (or more generally to any system call) 
 * is invalid. Think of it as a sort of SIGSEGV in kernel 
 * land regarding your syscall. For instance, if you pass 
 * a null or invalid buffer pointer (for reading, writing, sending, recieving...)。
 * 说明在对套接字上调用某些函数的时候传入了空指针等非法參数，的确如此。
*/
static ssize_t globalmemfifo_read (struct file * file, char __user * buff, size_t size, loff_t *offset)
{

    unsigned int count = size;
    ssize_t ret=0;
    struct globalmemfifo_dev *dev = file->private_data;
    DECLARE_WAITQUEUE(wait,current);//等待队列名称为wait, 
                                    /*
                                    static inline struct task_struct *get_current(void)
                                        {
                                            return current_thread_info()->task;
                                        }

                                        #define current get_current()
                                    */

     /**
      * count 合法性判断
      * 如果 当前需要读出数据大于了 当前已有大小则全部读出
     */
 
    mutex_lock(&dev->m_mutex);//进入互斥体
    add_wait_queue(&dev->r_wait,&wait);

    while(dev->current_len == 0){
        if(file->f_flags & O_NONBLOCK){
            ret = -EAGAIN;
            goto out;
        }
        __set_current_state(TASK_INTERRUPTIBLE);//设置等待队列状态
        mutex_unlock(&dev->m_mutex);
        schedule();//调度其他任务
        if(signal_pending(current)){
            ret = -ERESTARTSYS;
            goto out2;
        }
        mutex_lock(&dev->m_mutex);//进入互斥体
    }
    
    if(count > dev->current_len)
        count = dev->current_len;

    if(copy_to_user(buff,dev->mem,count)){
        ret  = -EFAULT;
        goto out;
    }else {
         memcpy(dev->mem,dev->mem+count,dev->current_len-count);
         dev->current_len-=count;

         printk("read %u bytes(s) from %x current_len :%d\n", 
                    count, count,dev->current_len);
    }    
    mutex_unlock(&dev->m_mutex);//退出互斥体
    wake_up_interruptible(&dev->w_wait); //唤醒写队列
    ret = count;
out:
    mutex_unlock(&dev->m_mutex);
out2:
    remove_wait_queue(&dev->r_wait,&wait);
    set_current_state(TASK_RUNNING);
    return ret;
}
/**
 * 写数据到设备中  返回0 end-of-file(EOF) 
*/
static ssize_t globalmemfifo_write (struct file *file, const char __user *buff, size_t size, loff_t *offset)
{
    unsigned int count = size;
    ssize_t ret=0;
    struct globalmemfifo_dev *dev = file->private_data;
    DECLARE_WAITQUEUE(wait,current);
    mutex_lock(&dev->m_mutex);
    add_wait_queue(&dev->w_wait,&wait);

    while(dev->current_len == GLOBAMEM_SIZE){
        if(file->f_flags & O_NONBLOCK){
            ret = -EAGAIN;
            goto out;
        }
        __set_current_state(TASK_INTERRUPTIBLE);
        mutex_unlock(&dev->m_mutex);
        schedule();

        if(signal_pending(current)){
            ret = -ERESTARTSYS;
            goto out2;
        }
        mutex_lock(&dev->m_mutex);
    }

    if(count > GLOBAMEM_SIZE-dev->current_len)
    count = GLOBAMEM_SIZE-dev->current_len;

    if(copy_from_user(dev->mem+dev->current_len,buff,count)){
        ret  = -EFAULT;
        goto out;
    }else {
        dev->current_len += count;
        wake_up_interruptible(&dev->r_wait); //wakt up read wait queue
        /*支持异步通知 写入数据时发送信号*/
        if(dev->async_queue){
            kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
            printk("%s kill SIGIO\n",__func__);
        }
        ret = count;
         printk("write %u bytes(s) from current_len = %d \n", count, dev->current_len);
    }  


out:
    mutex_unlock(&dev->m_mutex);
out2:
    remove_wait_queue(&dev->r_wait,&wait);
    set_current_state(TASK_RUNNING);
    return ret;
}
/**
 * 提供设备相关的控制命令  对应应用程序中的  int fcntl(int fd,int cmd,...)
*/
long globalmemfifo_unlocked_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
{
    struct globalmemfifo_dev *dev = file->private_data;
    switch (cmd)
    {
    case MEM_CLEN_CMD:
        mutex_lock(&dev->m_mutex);
        memset(dev->mem,0,GLOBAMEM_SIZE);
        dev->current_len = 0;
        mutex_unlock(&dev->m_mutex);
        printk("globalmemfifo set 0 form mem \n");
        break;
    
    default:
        return -EINVAL;
        break;
    }
    return 0;
}
/**
 * 对应应用程序的 open函数
 * 设置 node 节点的私有数据
*/
int globalmemfifo_open (struct inode *node, struct file *file)
{

    //container_of 通过结构体成员变量的地址，获取这个结构体的地址
    //这个地方的 node->i_cdev 也就是 globalmemfifo_dev 中的cdev
    /*container_of需要传入三个参数，第一个参数是一个指针，第二个参数是结构体类型，
                第三个是对应第二个参数里面的结构体里面的成员。

        container_of(ptr, type, member)

        ptr:表示结构体中member的地址 h
        type:表示结构体类型 struct sensor_private_data
        member:表示结构体中的成员 early_suspend type里面一定要有这个成员，
        返回结构体的首地址
    */
    struct globalmemfifo_dev *dev = container_of(node->i_cdev,
                                            struct globalmemfifo_dev,
                                            cdev);
    if(!dev){
        printk("%s not find globalmemfifo_dev \n",__func__);
        return  -ENOMEM; 
    }
    file->private_data = dev;
     printk("%s set globalmemfifo private data \n",__func__);
    return 0;
}


static unsigned int globalmemfifo_poll (struct file *filp, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    struct globalmemfifo_dev *dev = filp->private_data;
    mutex_lock(&dev->m_mutex);

    /*poll add wait queue*/
    poll_wait(filp,&dev->r_wait,wait);
    poll_wait(filp,&dev->w_wait,wait);

    /*set mask and return this mask for user*/
    if(dev->current_len != 0){
        mask |= POLLIN | POLLRDNORM; //file can read ,norm date can read 
    }

    if(dev->current_len != GLOBAMEM_SIZE){
        mask |= POLLOUT | POLLWRNORM; //file can write,norm data can write
    }

    mutex_unlock(&dev->m_mutex);
    return mask;
}

static int globalmemfifo_fasync (int fd, struct file *filp, int mode)
{
    struct globalmemfifo_dev *dev = filp->private_data;
    return fasync_helper(fd,filp,mode,&dev->async_queue);//init fasync
}

/**
 * 释放时调用
*/
int globalmemfifo_release (struct inode *node, struct file *file)
{
    globalmemfifo_fasync(-1,file,0);//释放驱动时需要将 异步通知中的文件删除
     printk("%s this function didn't anyting \n",__func__);
    return 0;
}
static const struct file_operations globalmemfifo_fops = {
    .owner = THIS_MODULE,
    .llseek = globalmemfifo_llseek, //用来修改文件的当前读写位置
    .read = globalmemfifo_read,    
    .write = globalmemfifo_write,
    .unlocked_ioctl = globalmemfifo_unlocked_ioctl, //提供设备相关的控制命令  用户调用 int fcnctl 函数时调用
    .open = globalmemfifo_open,
    .release = globalmemfifo_release,
    .poll = globalmemfifo_poll,
    .fasync = globalmemfifo_fasync,//支持异步通知
};
static void globalmemfifo_setup_cdev(struct globalmemfifo_dev *dev, int index){
    int err;
    /**
     * #define MKDEV(ma,mi)	((ma)<<8 | (mi))
    */
    int dev_node = MKDEV( globalmemfifo_major,index);
    /**
     * void cdev_init(struct cdev *cdev, const struct file_operations *fops)
    */
    cdev_init(&dev->cdev,&globalmemfifo_fops);
    dev->cdev.owner = THIS_MODULE;
    /**
     * int cdev_add(struct cdev *p, dev_t dev, unsigned count)
    */
    err = cdev_add(&dev->cdev,dev_node,1);
}
#ifdef PLATFORM_DRIVER
static int  globalmemfifo_prober(struct platform_device *pdev){
#else
static int __init  globalmemfifo_init(void){
#endif 
    int ret ;
    int i;
    dev_t dev_node = MKDEV(globalmemfifo_major,0);
    if(globalmemfifo_major){
        /**
         * int register_chrdev_region(dev_t from,unsigned count,const char* name) 
         * 用于已知设备的主设备号的情况下 注册字符设备驱动
        */
        ret =register_chrdev_region(dev_node,DEVICE_NUM,"globalmemfifo");    
    }else {
        /**
         * int alloc_chrdev_region(dev_t form,unsigned daseminor,unsigned cont,const char* name)
         * 向系统动态申请未被占用的设备号
        */
        ret = alloc_chrdev_region(&dev_node,0,DEVICE_NUM,"globalmemfifo");
        globalmemfifo_major = MAJOR(dev_node);
    }
    
    if(ret < 0)
     return ret;
    /**
     * #define GFP_KERNEL(__GFP_WAIT | __GFP_IO | __GFP_FS)
        __GFP_WAIT ： 缺内存页的时候可以睡眠;
        __GFP_IO ： 允许启动磁盘IO；
        __GFP_FS ： 允许启动文件系统IO。
    */
    globalmemfifo_devp = kzalloc(sizeof(struct globalmemfifo_dev)*DEVICE_NUM,GFP_KERNEL);
    if(!globalmemfifo_devp)
    {
        ret = -ENOMEM;
        goto fail_malloc;
    }
    
    for(i=0;i<DEVICE_NUM;i++){    
    mutex_init(&(globalmemfifo_devp+i)->m_mutex);
    init_waitqueue_head(&(globalmemfifo_devp+i)->r_wait);
    init_waitqueue_head(&(globalmemfifo_devp+i)->w_wait);
    globalmemfifo_setup_cdev(globalmemfifo_devp+i,i);//设置字符驱动
    }
    
    printk("%s init\n",__func__);
    return 0;
fail_malloc:
    unregister_chrdev_region(dev_node,1);
    return ret;
}

#ifdef PLATFORM_DRIVER
static int  globalmemfifo_remove(struct platform_device *pdev){
#else
static void __exit globalmemfifo_exit(void){
#endif 
    int i;
    for(i =0;i<DEVICE_NUM;i++)
        cdev_del(&(globalmemfifo_devp+i)->cdev);
    kfree(globalmemfifo_devp);
    unregister_chrdev_region(MKDEV(globalmemfifo_major,0),DEVICE_NUM);    
    printk("%s exit\n",__func__);

#ifdef PLATFORM_DRIVER
    return 0;
#else
    ;
#endif 
}



#ifdef PLATFORM_DRIVER
    static const struct of_device_id globalmemfifo_match[] = {
	{ .compatible = "globalmemfifo" },
	{},
}  ;
    static struct platform_driver globalmemfifo_driver ={
    .driver = {
        .name = "globalmemfifo",
        .owner = THIS_MODULE,
        .of_match_table = globalmemfifo_match,
    },
    .probe = globalmemfifo_prober,
    .remove= globalmemfifo_remove,
};  
 module_platform_driver(globalmemfifo_driver);
#else 
    module_init(globalmemfifo_init);
    module_exit(globalmemfifo_exit);
#endif 
MODULE_AUTHOR("302501638@qq.com");
MODULE_LICENSE("GPL v2");
